using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Microsoft.Data.Schema.Extensibility;
using Microsoft.Data.Schema.SchemaModel;
using Microsoft.Data.Schema.ScriptDom.Sql;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.Data.Schema.Sql;
using Microsoft.Data.Schema.StaticCodeAnalysis;
using System.Text.RegularExpressions;

using System.Linq;

namespace Neznayka
{
    /// <summary>
    /// This is a SQL rule which returns a warning message 
    /// whenever there is a ........................................
    /// This rule only applies to .................................
    /// </summary>

    [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
    [DataRuleAttribute(
        NeznaykaConstants.NameSpace,
        NeznaykaConstants.CheckForDuplicatedColumnsInIndexOrConstraintRuleId,
        NeznaykaConstants.ResourceBaseName,
        NeznaykaConstants.CheckForDuplicatedColumnsInIndexOrConstraint_RuleName,
        NeznaykaConstants.CategoryDatabaseStructures,
        DescriptionResourceId = NeznaykaConstants.CheckForDuplicatedColumnsInIndexOrConstraint_ProblemDescription)]
    [SupportedElementType(typeof(ISqlIndex))]
    [SupportedElementType(typeof(ISqlPrimaryKeyConstraint))]
    [SupportedElementType(typeof(ISqlUniqueConstraint))]
    public class CheckForDuplicatedColumnsInIndexOrConstraintRule : StaticCodeAnalysisRule
    {
        #region Overrides
        /// <summary>
        /// Analyze the model element
        /// </summary>
        public override IList<DataRuleProblem> Analyze(DataRuleSetting ruleSetting, DataRuleExecutionContext context)
        {
            // (Re)-Load Environment settings
            List<DataRuleProblem> problems;
            SqlSchemaModel sqlSchemaModel;
            ISqlModelElement sqlElement;
            TSqlFragment sqlFragment;
            DMVRuleSetup.RuleSetup(context, out problems, out sqlSchemaModel, out sqlElement, out sqlFragment);

            // Refresh cached index/constraints/tables lists from Model
            DMVRuleSetup.RefreshDDLCache(sqlSchemaModel);

            List<TSqlFragment> issues = new List<TSqlFragment>();

            ISqlIndex                       idx     = sqlElement as ISqlIndex;             
            ISqlPrimaryKeyConstraint        pk      = sqlElement as ISqlPrimaryKeyConstraint;
            ISqlUniqueConstraint            uk      = sqlElement as ISqlUniqueConstraint;

            List<ISqlIndexedColumnSpecification> colSpec = null;
            if (idx != null) {
                colSpec = idx.ColumnSpecifications.ToList();
            }
            else if (pk != null) {
                colSpec = pk.ColumnSpecifications.ToList();
            }
            else if (uk != null) {
                colSpec = uk.ColumnSpecifications.ToList();
            }

            if (colSpec != null)
            {
                List<String> cols = new List<String>();

                String lastElement = "";
                foreach (var c in colSpec)
                {
                    foreach (var n in c.Column.Name.Parts)
                    {
                        lastElement = n;
                    }
                    cols.Add(lastElement);
                }
                List<String> uniqCols = cols.Distinct().Select(n => n).ToList();

                if ( uniqCols.Count != cols.Count ) {
                    issues.Add(sqlFragment);
                }
            }

            // Create problems for each object
            foreach (TSqlFragment issue in issues)
            {
                DataRuleProblem problem = new DataRuleProblem(this,
                                            String.Format(CultureInfo.CurrentCulture, this.RuleProperties.Description, SqlRuleUtils.GetElementName(sqlSchemaModel, sqlElement)),
                                            sqlElement);

                SqlRuleUtils.UpdateProblemPosition(problem, issue.StartOffset, issue.FragmentLength);
                problems.Add(problem);
            }


            return problems;
        }

        #endregion    
 
    }
}
